/*
 *  SetSnapshot        14-nov-2009
 *   
 * Copyright (c) 2009 mentalsmash.org
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 *  The use of the Apache License does not indicate that this project is
 *  affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.transactions.objects;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import org.mentalsmash.documentation.Concurrency;
import org.mentalsmash.tazio.commons.identifiers.OID;
import org.mentalsmash.tazio.transactions.util.Utility;

import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;

/**
 * A <tt>SetSnapshot</tt> is a kind of snapshot capable of keeping snapshot data
 * for Transactional Sets.
 * <p>
 * A Transactional Set is a Set containing only {@link TransactionalObject}s or
 * primitive types.<br />
 * Therefore the snapshot of that set contains either the {@link OID}s of the
 * TransacitonalObjects, or the primitive instances themselves
 * </p>
 * 
 * @author Andrea Reale
 * @version 0.0.1
 */

@Concurrency(concurrentAccess = false, specification = "a SetSnapshot instance should be accept only by one thread,"
	+ " and that is the thread that owns the Transactions that ons the "
	+ "Private Snapshot. No synchronization is needed.")
class SetSnapshot implements Snapshot, Set<Serializable> {

    private static final long serialVersionUID = 3405237249977556035L;

    private final OID targetObject;
    private final long initialVersion;
    private final Set<Serializable> contents;

    /**
     * 
     * @param <E>
     *            the type of the Collection to be snapshoot. In the current
     *            implementation it can only be {@link OID} or a primitive type
     *            (as defined in {@link Utility#isPrimitive(Class)})
     * @param target
     *            the {@link OID} of the objects this snapshot is for
     * @param version
     *            the version of the object this snapshot is for, at the moment
     *            of the snapshot creation
     * @param collectionType
     *            one of {@link OID}<code>.class</code> or the class of a
     *            primitive type (as defined in
     *            {@link Utility#isPrimitive(Class)})
     * @return a new (empty) instance of <tt>SetSnapshot</tt>
     */
    public SetSnapshot(final OID target, final long version) {
	Preconditions.checkNotNull(target);
	// TODO : set this to version > 0 (and make it work)
	Preconditions.checkArgument(version >= 0);

	this.targetObject = target;
	this.initialVersion = version;
	this.contents = Sets.newHashSet();
    }

    @Override
    public long getInitialVersion() {
	return this.initialVersion;
    }

    @Override
    public OID getOID() {
	return this.targetObject;
    }

    @Override
    public boolean isSetSnapshot() {
	return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#add(java.lang.Object)
     */
    @Override
    public boolean add(final Serializable e) {
	Preconditions.checkArgument(
		(Utility.isPrimitiveType(e) || (e instanceof OID)),
		"Argument must be either a primitive type or an OID");
	return contents.add(e);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#addAll(java.util.Collection)
     */
    @Override
    public boolean addAll(final Collection<? extends Serializable> c) {
	if (!c.isEmpty()) {
	    final Serializable sample = c.iterator().next();
	    Preconditions
		    .checkArgument(
			    (Utility.isPrimitiveType(sample) || (sample instanceof OID)),
			    "Argument must be either a primitive type or an OID");
	}

	return contents.addAll(c);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#clear()
     */
    @Override
    public void clear() {
	contents.clear();

    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#contains(java.lang.Object)
     */
    @Override
    public boolean contains(final Object o) {
	return contents.contains(o);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#containsAll(java.util.Collection)
     */
    @Override
    public boolean containsAll(final Collection<?> c) {
	return contents.containsAll(c);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#isEmpty()
     */
    @Override
    public boolean isEmpty() {
	return contents.isEmpty();
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#iterator()
     */
    @Override
    public Iterator<Serializable> iterator() {
	return contents.iterator();
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#remove(java.lang.Object)
     */
    @Override
    public boolean remove(final Object o) {
	return contents.remove(o);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#removeAll(java.util.Collection)
     */
    @Override
    public boolean removeAll(final Collection<?> c) {
	return contents.removeAll(c);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#retainAll(java.util.Collection)
     */
    @Override
    public boolean retainAll(final Collection<?> c) {
	return contents.retainAll(c);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#size()
     */
    @Override
    public int size() {
	return contents.size();
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#toArray()
     */
    @Override
    public Object[] toArray() {
	return contents.toArray();
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Set#toArray(T[])
     */
    @Override
    public <E> E[] toArray(final E[] a) {
	return contents.toArray(a);
    }

}
